import { createEventHook } from '@vueuse/core'
import * as THREE from 'three'
import { Mesh } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import {
  CSS2DObject,
  CSS2DRenderer,
} from 'three/examples/jsm/renderers/CSS2DRenderer'
import { v4 as uuidv4 } from 'uuid'
import { onBeforeUnmount, onMounted, Ref, ref, shallowRef, watch } from 'vue'

import { EnumLabelType } from '../config/config'

export interface ILabel {
  id: string
  position: {
    x: number
    y: number
    z: number
  }
  name: string
  content: string
  type: EnumLabelType
  radius: number
  filePath: string
  backgroundColor: string
  label: CSS2DObject
  marker: Mesh
}

export function useThree(
  containerRef: Ref<HTMLDivElement | undefined>,
  enabledControls: Ref<boolean>,
) {
  const raycaster = new THREE.Raycaster()
  const mouse = new THREE.Vector2()
  const status = ref<'start' | 'end'>('start')
  const width = ref(100)
  const height = ref(100)
  const controls = shallowRef<OrbitControls | null>(null)
  const renderer = shallowRef<THREE.WebGLRenderer | null>(null)
  const css2DRenderer = shallowRef<CSS2DRenderer | null>(null)
  const scene = shallowRef<THREE.Scene | null>(null)
  const camera = shallowRef<THREE.PerspectiveCamera | null>(null)
  const threeModel = shallowRef<THREE.Object3D | null>(null)
  const labels = ref<ILabel[]>([])
  const onClickModel = createEventHook<{
    x: number
    y: number
    z: number
  }>()

  watch(enabledControls, (newValue) => {
    if (controls.value) {
      controls.value.enabled = newValue
    }
  })
  const modelCenter = ref({
    x: 0,
    y: 0,
    z: 0,
  })
  const modelSize = ref({
    x: 0,
    y: 0,
    z: 0,
  })

  function animate() {
    // renderer.value?.setClearColor(0xf0f0f0, 1)
    renderer.value?.setScissorTest(true)
    renderer.value?.setScissor(0, 0, width.value, height.value)
    renderer.value?.setViewport(0, 0, width.value, height.value)
    renderer.value?.render(scene.value!, camera.value!)
    css2DRenderer.value?.render(scene.value!, camera.value!) // 关键：单独渲染标签
    renderer.value?.setScissorTest(false)
    controls.value?.update()
  }
  const init = () => {
    width.value = containerRef.value?.clientWidth || 100
    height.value = containerRef.value?.clientHeight || 100
    camera.value = new THREE.PerspectiveCamera(
      45,
      width.value / height.value,
      0.1,
      10000,
    )
    camera.value.viewport = new THREE.Vector4(0, 0, width.value, height.value)

    // 主相机图层设置
    camera.value.layers.enable(0)
    camera.value.layers.enable(1)
    camera.value.layers.enable(2)

    scene.value = new THREE.Scene()
    // scene.value.background = new THREE.Color(0xf0f0f0)

    const ambientLight = new THREE.AmbientLight(0xffffff, 1)
    scene.value.add(ambientLight)

    const pointLight = new THREE.PointLight(0xffffff, 1)
    camera.value.add(pointLight)

    renderer.value = new THREE.WebGLRenderer({ antialias: true, alpha: true })
    renderer.value.setPixelRatio(window.devicePixelRatio)
    renderer.value.setSize(width.value, height.value)
    renderer.value.setAnimationLoop(animate)
    containerRef.value?.appendChild(renderer.value.domElement)

    css2DRenderer.value = new CSS2DRenderer()
    css2DRenderer.value.setSize(width.value, height.value)
    css2DRenderer.value.domElement.style.position = 'absolute'
    css2DRenderer.value.domElement.style.top = '0'
    containerRef.value?.appendChild(css2DRenderer.value.domElement)

    controls.value = new OrbitControls(camera.value, containerRef.value)
    controls.value.enableDamping = true
    controls.value.enabled = false
    controls.value.minDistance = 0.2
  }

  const addModel = (model: THREE.Object3D) => {
    threeModel.value = model
    // 1. 计算模型的世界包围盒
    const bbox = new THREE.Box3().setFromObject(threeModel.value)
    const size = bbox.getSize(new THREE.Vector3())

    // 2. 计算模型底部需要补偿的高度
    // const modelBottomY = bbox.min.y // 模型最低点的Y坐标
    // const offsetY = -modelBottomY // 需要向上移动的距离

    // 3. 调整模型位置
    // threeModel.value.position.y += offsetY

    const newBbox = new THREE.Box3().setFromObject(threeModel.value)
    const newCenter = newBbox.getCenter(new THREE.Vector3())

    modelCenter.value = {
      x: newCenter.x,
      y: newCenter.y,
      z: newCenter.z,
    }
    modelSize.value = {
      x: size.x,
      y: size.y,
      z: size.z,
    }

    const maxSize = Math.max(size.x, size.y, size.z)
    const cameraDistance = maxSize * 3 // 2倍模型尺寸的距离

    camera.value?.position.copy(newCenter)
    camera.value?.position.set(
      newCenter.x + cameraDistance * Math.sin(Math.PI / 6),
      newCenter.y + cameraDistance * Math.sin(Math.PI / 6),
      newCenter.z + cameraDistance * Math.cos(Math.PI / 6),
    )

    camera.value?.lookAt(newCenter)

    // 4. 添加到场景
    threeModel.value.layers.enable(0)

    scene.value?.add(threeModel.value)

    // const gridSize = maxSize * 5
    // const divisions = 10

    // const gridHelper = new THREE.GridHelper(gridSize, divisions, '', '#000000')
    // scene.value?.add(gridHelper)
  }
  const deleteLabel = (id: string) => {
    const index = labels.value.findIndex((item) => item.id === id)
    if (index <= -1) return
    const label = labels.value[index]
    label.label.removeFromParent()
    scene.value?.remove(label.label)

    scene.value?.remove(label.marker)
    label.marker.geometry.dispose()
    if (label.marker.material) {
      // 处理材质数组
      if (Array.isArray(label.marker.material)) {
        label.marker.material.forEach((m) => m.dispose())
      } else {
        label.marker.material.dispose()
      }
    }
    label.marker.removeFromParent()
    labels.value.splice(index, 1)
  }
  const removeLabel = (id: string) => {
    deleteLabel(id)
  }
  const clearAllLabels = () => {
    labels.value.forEach((item) => {
      deleteLabel(item.id)
    })
  }
  const addLabel = (
    id: string = uuidv4(),
    pos: {
      x: number
      y: number
      z: number
    },
    name: string,
    type: EnumLabelType,
    content: string,
    backgroundColor: string,
    radius: number,
    filePath: string,
  ) => {
    const position = new THREE.Vector3(pos.x, pos.y, pos.z)
    const labelDiv = document.createElement('div')
    labelDiv.className = 'label'
    labelDiv.textContent = name
    labelDiv.style.backgroundColor = backgroundColor
    labelDiv.style.padding = '5px 10px'
    labelDiv.style.color = 'white'
    labelDiv.style.borderRadius = `${radius}px`
    labelDiv.style.maxWidth = '200px'
    labelDiv.style.wordBreak = 'break-all'
    labelDiv.id = id

    const markerSize =
      Math.min(modelSize.value.x, modelSize.value.y, modelSize.value.z) / 100

    // 创建3D标签对象
    const label = new CSS2DObject(labelDiv)
    label.position.copy(position)
    label.position.y += markerSize * 2 // 向上偏移避免遮挡
    console.log('label', label)
    scene.value?.add(label)

    const marker = new THREE.Mesh(
      new THREE.SphereGeometry(markerSize, 16, 16),
      new THREE.MeshBasicMaterial({
        color: 0xff0000,
      }),
    )
    marker.position.copy(position)
    scene.value?.add(marker)
    const labelData = {
      id,
      position: position.clone(),
      name,
      type,
      content,
      backgroundColor,
      radius,
      label,
      marker,
      filePath,
    }
    labels.value.push(labelData)

    // const line = new THREE.Line(
    //   new THREE.BufferGeometry().setFromPoints([
    //     position,
    //     position.clone().add(new THREE.Vector3(0, 0.5, 0)),
    //   ]),
    //   new THREE.LineBasicMaterial({
    //     color: 0x00ff00,
    //     linewidth: 1, // 线宽（需要启用抗锯齿才有效）
    //     // depthWrite: false,
    //     // depthTest: false, // 关闭深度测试
    //   }),
    // )
    // line.renderOrder = 1
    // scene.value?.add(line)
  }

  const onResize = () => {
    width.value = containerRef.value?.clientWidth || 100
    height.value = containerRef.value?.clientHeight || 100
    // 重置渲染器输出画布canvas尺寸
    renderer.value?.setSize(width.value, height.value)
    // 全屏情况下：设置观察范围长宽比aspect为窗口宽高比
    camera.value!.aspect = width.value / height.value
    // 如果相机视锥体相关参数发生了变化，需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
    camera.value!.updateProjectionMatrix()
  }
  const onMouseClick = (event: MouseEvent) => {
    console.log(event)
    if (enabledControls.value) return
    if (status.value === 'start') return
    if (!threeModel.value) return
    const rect = renderer.value?.domElement.getBoundingClientRect()
    if (!rect) return
    mouse.x = ((event.clientX - rect.left) / width.value) * 2 - 1
    mouse.y = -((event.clientY - rect.top) / height.value) * 2 + 1
    raycaster.setFromCamera(mouse, camera.value!)
    const intersects = raycaster.intersectObjects(
      threeModel.value.children,
      true,
    )
    if (intersects.length > 0) {
      const point = intersects[0].point
      onClickModel.trigger(point)
    }
  }

  onMounted(() => {
    init()
    status.value = 'end'
    window.addEventListener('resize', onResize)
    containerRef.value?.addEventListener('pointerdown', onMouseClick)
  })

  onBeforeUnmount(() => {
    if (renderer.value) {
      renderer.value.dispose()
      if (renderer.value.domElement.parentNode) {
        renderer.value.domElement.parentNode.removeChild(
          renderer.value.domElement,
        )
      }
    }
    window.removeEventListener('resize', onResize)
    containerRef.value?.removeEventListener('pointerdown', onMouseClick)
  })

  return {
    threeModel,
    modelSize,
    modelCenter,
    renderer,
    scene,
    camera,
    controls,
    addModel,
    addLabel,
    clearAllLabels,
    removeLabel,
    labels,
    onClickModel: onClickModel.on,
  }
}
